#pragma once

#include <Scanner/scanner.h>
#include <Compiler/scope.h>
#include <functional>
#include <string>
#include <optional>
#include <unordered_map>

class ErrorReporter;

class Compiler
{
public:
	explicit Compiler(ErrorReporter &reporter) : reporter(reporter) {}
	FunctionPtr compile(std::string_view source, int line);
	void reset();

private:
	ErrorReporter &reporter;
	Scanner scanner;
	std::unique_ptr<Scope> scope;

	Token peek; // 将要处理的Token，需要用advance跳过

	struct Instruction
	{
		OpCode op;
		Token tok;
		std::optional<u8_t> arg;
	};

	// 用于延缓parseIdentifier中发射Get信号
	// 返回到parseAssign中确认前段是有效的赋值信息
	std::optional<Instruction> delayGet;

	// 记录Break语句，在循环体中Patch
	std::vector<size_t> unpatchedBreaks;

	size_t innerMostLoopDepth = 0;
	int innerMostLoopStart = -1; // Continue语句跳转位置

private:
	bool isAtEnd();
	bool peekIs(TokenType type);
	template <typename... U>
	bool peekIs(TokenType t, U... u)
	{
		if (sizeof...(u) == 0)
			return peekIs(t);
		else
		{
			return peekIs(t) || peekIs(u...);
		}
	}

	Token advance();
	bool advanceIf(TokenType type);
	Token expect(TokenType type, std::string errorMessage);
	void synchronize(); // 当捕获到compiler error时，尝试继续分析

	// byte code relevant
	void emit(OpCode code, const Token &tok, std::optional<u8_t> argument = std::nullopt);
	size_t emitConstant(Value val, const Token &tok);
	void emitDelayGet();
	void emitPop();
	size_t emitJump(OpCode code, const Token &tok);
	void patchJump(int offset);
	void emitLoop(int loopStart, const Token &tok);
	void emitReturn();

	// Parsing relevant

	// Declarations
	void parseDeclaration();

	void parseFuncDeclStmt();
	void parseFuncBody(FunctionType type, const Token &name);

	void parseVarDeclStmt();
	void declareVariable(const Token &name);
	void defineVariable(const Token &name);

	// Statements
	void parseStatement();

	void parseIfStmt();
	void parseForStmt();
	void parseWhileStmt();
	void parseBreakStmt();
	void parseContinueStmt();
	void parseReturnStmt();
	void parseBlock();
	void parseExpressionStmt();

	// Expressions
	void parseExpression();

	void parseComma();		// a,b,c
	void parseAssign();		// a = 1;
	void parseTernary();	// ? :
	void parseOr();			// or
	void parseAnd();		// and
	void parseEquality();	// "==" || "!="
	void parseComparison(); // ">=" || "<=" || '>' || '<'
	void parseTerm();		// '+' || '-'
	void parseFactor();		// '*' || '/'
	void parseUnary();		// '-' || '!'
	void parsePrefix();		// "++a" || "--a"
	void parsePostfix();	// "a++" || "a--"
	void parseCall();		// () || [] || .
	u8_t parseArgument(TokenType ending);

	void parsePrimary();
	void parseIdentifier();
	void parseString();
	void parseGroup(); // (...)
	void parseNumber();

	// Help function for parsing
	void parseBinary(const std::function<void(Compiler *)> &operand, const std::unordered_map<TokenType, OpCode> &ops);
	void handleIDCrement(OpCode op, bool postfix = false);
};